/**
 * 
 */
package com.williamoneil.charts.server.controller;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;

import com.williamoneil.charts.server.controller.msn.MSNEarningsController;
import com.williamoneil.charts.server.controller.yahoo.YahooPriceVolumeController;
import com.williamoneil.charts.shared.ChartData;
import com.williamoneil.charts.shared.FinancialData;
import com.williamoneil.charts.shared.PeriodicityType;
import com.williamoneil.charts.shared.PriceVolumeData;
import com.williamoneil.charts.shared.TradeDateType;

/**
 * @author Gudipati
 *
 */
public class ChartController {
	private final YahooPriceVolumeController pvc = new YahooPriceVolumeController();
	private final MSNEarningsController ec = new MSNEarningsController();
	
	private ChartController(){
	}
	
	private static ChartController _instance = null;
	public static final ChartController getInstance() {
		if(_instance == null) {
			_instance = new ChartController();
		}
		
		return _instance;
	}
	
	public ChartData getChartData(String symbol, PeriodicityType periodicity, int numNodes, Date endDt) throws Exception {
		if(symbol == null) {
			return null;
		}
		
		// upper-case and trim the symbol
		symbol = symbol.trim().toUpperCase();
		
		final ChartData data = new ChartData();
		
		// defaults to weekly
		if(periodicity == null) {
			periodicity = PeriodicityType.Weekly;
		}
		
		// use today's date if end-date is not specified.
		final Calendar endCal = Calendar.getInstance();
		if(endDt != null) {
			endCal.setTime(endDt);
		}
		
		final DateTime endDtTime = new DateTime(endCal.getTime());
		
		DateTime startDtTime = null;
		if(periodicity == PeriodicityType.Monthly) {
			if(numNodes < 1) {
				numNodes = 300;
			}
			
			startDtTime = endDtTime.minusMonths(numNodes);
		} else if (periodicity == PeriodicityType.Weekly) {
			if(numNodes < 1) {
				numNodes = 300;
			}
			
			startDtTime = endDtTime.minusWeeks(numNodes);
		} else if (periodicity == PeriodicityType.Daily) {
			if(numNodes < 1) {
				numNodes = 500;
			}
			
			// 500 nodes means, 700 weekdays
			startDtTime = endDtTime.minusDays(numNodes * 7 / 5);
		}
		
		// get p-v data from server
		final List<PriceVolumeData> pvList = pvc.getPriceVolume(symbol, periodicity, startDtTime.toGregorianCalendar(), endDtTime.toGregorianCalendar());
		if(pvList.size() < numNodes) {
			DateTime lastPvDate = new DateTime(pvList.get(pvList.size() - 1).getTradeDateAsDate().getTime());
			
			final int missingNodes = numNodes - pvList.size();
			for(int i=0;i<missingNodes;i++) {
				final PriceVolumeData aPV = new PriceVolumeData();
				aPV.setTradeDateType(TradeDateType.NO_TRADE);
				
				DateTime newDt = null;
				switch(periodicity) {
				case Daily: {
					final int dayOfWeek = lastPvDate.getDayOfWeek();
					if(dayOfWeek == DateTimeConstants.MONDAY) {
						newDt = lastPvDate.minusDays(3); 
					} else if(dayOfWeek == DateTimeConstants.SUNDAY) {
						newDt = lastPvDate.minusDays(2); 
					} else {
						newDt = lastPvDate.minusDays(1); 
					}
					break;
				}
				case Weekly: {
					newDt = lastPvDate.minusWeeks(1);
					newDt = newDt.withDayOfWeek(DateTimeConstants.FRIDAY);
					break;
				}
				case Monthly: {
					newDt = lastPvDate.minusMonths(1);
					int maxDayInMonth = newDt.dayOfMonth().getMaximumValue();
					newDt = newDt.withDayOfMonth(maxDayInMonth);
					break;
				}
				}
				
				aPV.setTradeDate(newDt.toLocalDate().toDate().getTime());
				lastPvDate = newDt;
				
				pvList.add(aPV);
			}
		}
		data.setPrices(pvList);
		
		final List<FinancialData> fins = ec.getFinancialData(symbol);
		data.setFinancialData(fins);
		
		return data;
	}
	
}
